home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------------------------------------
- JuggleInterface.c
-
- Routines that deal with the mouse, and routines that convert
- between screen coords and position in memory
-
- ----------------------------------------------------------------------*/
-
- #include "PaperJuggling.h"
- #include "DialogUtils.h"
-
- //----------------------------------------------------
- // Global for double click detection
- static long lastWhen = 0;
-
- //----------------------------------------------------
- // mouse handling routines
- void DoJuggleClick(WindowPtr window, Point p, EventRecord *event)
- {
- JuggleHandle aJuggle = GetWindowJuggle(window);
- gxPoint clickPt;
- gxHitTestInfo result;
- gxShape shapeHit;
- Boolean doubleClick = false;
- gxMapping portMapping;
-
- // get mouse position in viewPort
- ShortPointToFixed(&p, &clickPt);
- GXGetViewPortMapping(GetWindowGXPort(window), &portMapping);
- InvertMapping(&portMapping, &portMapping);
- MapPoints(&portMapping, 1, &clickPt);
-
- // Check for double click
- if ( event->when - lastWhen < GetDblTime() )
- {
- doubleClick = true; // it's a double click
- lastWhen = 0; // so triple clicks aren't seen as two doubles
- }
- else
- lastWhen = event->when;
-
- // Look for a hit on a throw
- shapeHit = GXHitTestPicture(GetDocThrowsPict(window),
- &clickPt,
- &result, 0, 1);
- // If a hit, drag the throw
- if (shapeHit != nil)
- {
- // Mark dirty
- (*aJuggle)->dirty = true;
-
- // If option key is down, remove it instead
- if(event->modifiers & optionKey)
- {
- gxLine lineData;
-
- // Remove from connections data
- GXGetLine(shapeHit, &lineData);
- LineToRemovedThrow(aJuggle, &lineData);
-
- // Erase shape and remove from picture(s)
- RemoveThrowShape(aJuggle, result.containerIndex);
- }
- else // drag it
- {
- // if it's not point 1 (the tail), drag point 2 (the arrow head)
- // (so clicks on the line itself drag the head)
- DragThrow(aJuggle, result.containerIndex, result.index, &clickPt);
- }
- }
- // Else look for a hit on a juggler
- else
- {
- HandPtr hitHand;
- HandLoc aLoc = {0, 0};
- gxPoint savedPt, grid = (*aJuggle)->gridPt;
- Boolean gridded;
-
- // if it's close enough to a grid point
- savedPt = clickPt;
- gridded = Detent(&clickPt, &grid, kHandRadius);
- if(gridded)
- {
- // get the handLoc and a pointer to the hand (don't wrap!)
- PointToHandLoc(aJuggle, &clickPt, &aLoc, false);
- hitHand = GetHand(aJuggle, aLoc, false);
-
- // if a hand or label was hit, do stuff
- if(aLoc.time != 0)
- {
- gxShape hitJuggler;
-
- // Mark dirty
- (*aJuggle)->dirty = true;
-
- // get the juggler picture
- GetPictureItem((*aJuggle)->jugglersPict,
- JugglerToRow(aJuggle, aLoc.juggler),
- &hitJuggler, nil, nil, nil);
-
- // If command key is down, shift juggler in time
- if(event->modifiers & cmdKey)
- {
- DragJuggler(window, hitJuggler, &clickPt,
- aLoc.juggler, hAxisOnly);
- }
- // else if the option key is down, remove juggler
- else if(event->modifiers & optionKey)
- {
- //RemoveJuggler(window, aLoc.juggler);
- }
- else if (hitHand != nil) // a hand was hit
- {
- // If the hand has no sink yet, assume the user wants
- // to add one.
- if(hitHand->sink.time == 0)
- {
- DragNewThrow(window, &clickPt);
- }
- }
- else if(aLoc.time == -1 && aLoc.juggler > 0) // label hit
- {
- // If a double click, reverse hands and redraw
- if(doubleClick)
- {
- SwitchJugglerHands(aJuggle, aLoc.juggler);
- EraseRect(&window->portRect);
- InvalRect(&window->portRect);
- }
- else
- {
- // drag juggler vertically
- DragJuggler(window, hitJuggler, &clickPt, aLoc.juggler, vAxisOnly);
- }
- }
- }
- }
- }
- }
-
- // Drag around a gray outline of the juggler, using QuickDraw
- void DragJuggler(WindowPtr window, gxShape jugglerPict, gxPoint *clickPt,
- short whichJuggler, short constraint)
- {
- RgnHandle rgn;
- Point shortGrid, qdOrigin, resultPt, qdClick;
- Rect limitRect, slopRect;
- JuggleHandle aJuggle;
- gxPoint gridPt;
- gxShape hand;
-
- aJuggle = GetWindowJuggle(window);
- gridPt = (*aJuggle)->gridPt;
-
- // Set up the grid
- FixedPointToShort(&gridPt, &shortGrid);
-
- // Set the QD origin to reflect the docPort's mapping
- qdOrigin = GetQDWindowOrigin(window);
- SetOrigin(qdOrigin.h, qdOrigin.v);
-
- // the qd mouse is converted from a viewPort mouse, so
- // no need to adjust it
- FixedPointToShort(clickPt, &qdClick);
-
- if(constraint == vAxisOnly)
- {
- // Get a rgn representing the bounds of the juggler
- rgn = GetShapeBoundsRgn(jugglerPict);
-
- // Set up limit and slop rects
- slopRect = window->portRect;
- GetShapeBoundsQDRect(GetWindowGXShape(window), &limitRect);
- }
- else // assume horizontal
- {
- // Get a rgn representing the bounds of a hand
- GetPictureItem(jugglerPict, 2, &hand, nil, nil, nil);
- hand = GXCopyToShape(nil, hand);
- MoveShapeCenterTo(hand, clickPt->x, clickPt->y);
- rgn = GetShapeBoundsRgn(hand);
- GXDisposeShape(hand);
-
- // Set up limit and slop rects
- slopRect = window->portRect;
- limitRect = (*rgn)->rgnBBox;
- InsetRect(&limitRect, -shortGrid.h, 0);
- }
-
- // drag the rgn around, gridded
- resultPt = DragGrayRgnGridded(rgn, qdClick,
- &limitRect, &slopRect, constraint, shortGrid, nil);
-
- // Clean up and restore origin
- DisposeRgn(rgn);
- SetOrigin(0, 0);
-
- // See what happened. If the drag ended outside the sloprect, do nothing
- if(resultPt.h != 0x8000 || resultPt.v != 0x8000)
- {
- // Move the juggler appropriately
- MoveJuggler(aJuggle, whichJuggler, resultPt.v / FixedRound(gridPt.y));
-
- // rebuild throws and force a redraw
- RebuildThrowsPict(aJuggle);
- SetPort(window);
- EraseRect(&window->portRect);
- InvalRect(&window->portRect);
- }
- }
-
- // This routine adds a new throw shape, tracks the mouse until the button is released,
- // then adds the new throw to the connections data
- void DragNewThrow(WindowPtr window, gxPoint *from)
- {
- JuggleHandle aJuggle;
- gxLine line;
- gxPoint jugglerGrid;
- HandLoc destLoc, sourceLoc;
-
- aJuggle = GetWindowJuggle(window);
- jugglerGrid = (*aJuggle)->gridPt;
-
- // Force starting geometry to the nearest grid point
- Detent(from, &jugglerGrid, jugglerGrid.x / 2);
- line.first = line.last = *from;
-
- // Convert point to hand location
- PointToHandLoc(aJuggle, &line.first, &sourceLoc, false);
- if(sourceLoc.time > 0)
- {
- // Look for the nearest legal receiver
- destLoc = FindReceiver(aJuggle, sourceLoc);
- if(destLoc.time > 0)
- {
- // Set new receiver as the line's end.
- HandLocToPoint(aJuggle, &destLoc, &line.last);
-
- // Make it a presentable line, off the hand a bit
- line.first.x += kHandRadius;
- line.last.x -= kHandRadius;
-
- // Add the new throw to the throws pict, and draw it
- AddNewThrowShape(aJuggle, &line, true);
-
- // Add to connections data to start
- LineToAddedThrow(aJuggle, &line);
-
- // Track the drag: the arrow is index 1 in the picture, we're dragging the arrow head
- DragThrow(aJuggle, 1, 2, from);
- }
- else
- SysBeep(10); // No legal receiver
- }
- else
- SysBeep(10); // point isn't on a hand
- }
-
- // Drag the throw by the indicated point: 1 is first, 2 is last.
- // Assumes that the transfer mode is xor
- void DragThrow(JuggleHandle aJuggle, long shapeIndex,
- long whichPoint, gxPoint *from)
- {
- gxLine lineData;
- gxShape throwShape;
- gxPoint rawPt, goodPt, lastPt, lastGoodPt, detentGrid;
- Fixed handRadius, twoHands;
- gxViewPort port;
- Boolean legalThrow;
-
- // init some variables
- detentGrid = (*aJuggle)->gridPt;
- port = (*aJuggle)->docPort;
- legalThrow = true; // The throw starts off legal by definition
- handRadius = kHandRadius;
- twoHands = 2 * handRadius;
-
- // Get current mouse, set starting positions
- GXGetViewPortMouse(port, &rawPt);
- lastPt = lastGoodPt = rawPt;
- KeepPtInBounds(aJuggle, &lastGoodPt);
-
- // Get the geometry of the throw
- GetPictureItem((*aJuggle)->throwsPict, shapeIndex, &throwShape, nil, nil, nil);
- GXGetLine(throwShape, &lineData);
-
- // Remove it from the connections data while dragging
- LineToRemovedThrow(aJuggle, &lineData);
-
- // Track the mouse, erasing and redrawing
- while(StillDown())
- {
- // Get the new mouse position and if it's different,
- // adjust the dragged shape
- GXGetViewPortMouse(port, &rawPt);
- if(rawPt.x != lastPt.x || rawPt.y != lastPt.y)
- {
- HandPtr hand = nil;
- Fixed maxLength, minLength;
-
- // assume illegal throw
- legalThrow = false;
-
- // max line length is one cycle minus 2 hands
- maxLength = (detentGrid.x * ((*aJuggle)->numCounts)) - twoHands;
-
- // min length is one grid minus 2 hands
- minLength = detentGrid.x - twoHands;
-
- // keep the point in bounds
- goodPt = rawPt;
- KeepPtInBounds(aJuggle, &goodPt);
-
- // dragging the tail
- if(whichPoint == 1)
- {
- // tail point, must be earlier in time than line's last point
- // by at least minLength
- if(goodPt.x > lineData.last.x - minLength )
- goodPt.x = lineData.last.x - minLength;
- // and line not longer than maxLength
- else if(goodPt.x < lineData.last.x - maxLength)
- goodPt.x = lineData.last.x - maxLength;
-
- // also must stay in range: throws MUST originate
- // from inside the unfaded parts
- if(goodPt.x > maxLength)
- goodPt.x = maxLength;
-
- lineData.first = goodPt; // use this point
- }
- else // dragging arrow head
- {
- // head must be later in time than line's first point
- // by at least minLength
- if(goodPt.x < lineData.first.x + minLength)
- goodPt.x = lineData.first.x + minLength;
-
- // but not longer than maxLength
- else if(goodPt.x > lineData.first.x + maxLength)
- goodPt.x = lineData.first.x + maxLength;
-
- lineData.last = goodPt; // use this point
- }
-
- // Check if it's near a grid point
- if(Detent(&goodPt, &detentGrid, kThrowDetent))
- {
- // It is, get the hand
- hand = PointToHand(aJuggle, &goodPt, true);
- if(hand != 0)
- {
- if(whichPoint == 1) // if dragging tail
- {
- if(hand->sink.time == 0) // and hand needs a sink
- {
- lineData.first = goodPt; // Legal change, so change it
- lineData.first.x += handRadius; // get it off the hand
- legalThrow = true;
- }
- else
- {
- // !!! repel?
- // Hand doesn't need a sink, restore goodPt to pre-Detent value
- goodPt = lineData.first;
- }
- }
- else // dragging arrow head
- {
- if(hand->source.time == 0) // if hand needs a source
- {
- lineData.last = goodPt; // Legal change, so change it
- lineData.last.x -= handRadius; // get it off the hand
- legalThrow = true;
- }
- else
- {
- // !!! repel?
- // Hand doesn't need a source, restore goodPt to pre-Detent value
- goodPt = lineData.last;
- }
- }
- }
- }
-
- // save last point
- lastPt = rawPt;
-
- // Only redraw if the new point is different than the last
- if(goodPt.x != lastGoodPt.x || goodPt.y != lastGoodPt.y)
- {
- // Erase old and draw new
- MoveThrowShape(aJuggle, shapeIndex, &lineData);
-
- // Save this good point
- lastGoodPt = goodPt;
- }
-
- } // if mouse moved
- } // while still down
-
- // Mouse button released. If a legal throw, add it back in to connections data
- if(legalThrow)
- LineToAddedThrow(aJuggle, &lineData);
- else // if illegal, erase and remove from the pictures
- {
- RemoveThrowShape(aJuggle, shapeIndex);
- }
- }
-
- // Like DragGrayRgn, but the region is constrained to grid points
- Point DragGrayRgnGridded(RgnHandle theRgn, Point startPt, Rect *limitRect,
- Rect *slopRect, short axis, Point grid, Point *initialOffset)
- {
- Point result, goodPt, rawMouse, lastGoodPt, lastRawMouse,
- gridOffset, startGridOffset;
- Boolean outsideSlop;
- PenState savedPen;
-
- // Initialize variables
- if(initialOffset != nil)
- AddPt(*initialOffset, &startPt);
-
- goodPt = lastGoodPt = rawMouse = lastRawMouse = startPt; // All the same to start
- result.h = result.v = 0;
- outsideSlop = false;
-
- // Save the pen state, set mode to xor
- GetPenState(&savedPen);
- PenNormal();
- PenMode(srcXor);
- PenPat(&qd.gray);
-
- // Draw the rgn to start
- FrameRgn(theRgn);
-
- // We are gridding the rgn, not the point itself, so we need to remember
- // the amount the original point was off the grid.
- startGridOffset = startPt;
- GetShortGridOffset(&startGridOffset, grid);
-
- // Track the mouse
- while(StillDown())
- {
- // Get the current mouse point.
- GetMouse(&rawMouse);
-
- if(initialOffset != nil)
- AddPt(*initialOffset, &rawMouse);
-
- // If the mouse moved, do stuff . . .
- if(rawMouse.h != lastRawMouse.h || rawMouse.v != lastRawMouse.v)
- {
- // if it's inside the slopRect...
- if(PtInRect(rawMouse, slopRect))
- {
- // if it's been outside the slopRect but is back inside…
- if(outsideSlop)
- {
- // …redraw it, and forget it was outside
- FrameRgn(theRgn);
- outsideSlop = false;
- }
-
- // We'll calculate a "good" (legal) point from the raw mouse
- goodPt = rawMouse;
-
- // if it's outside the limitRect but inside the slopRect...
- if(!PtInRect(goodPt, limitRect))
- {
- long temp;
-
- // ...pin the point to the limitRect
- temp = PinRect(limitRect, goodPt);
- goodPt = *(Point *)(&temp);
- }
-
- // Constrain the point to the grid, then add back the original offset,
- // so we keep the same offset from the grid points
- gridOffset = goodPt;
- GetShortGridOffset(&gridOffset, grid);
- goodPt.h = goodPt.h - gridOffset.h + startGridOffset.h;
- goodPt.v = goodPt.v - gridOffset.v + startGridOffset.v;
-
- // if it's axis constrained, constrain it
- if(axis != noConstraint)
- {
- if(axis == hAxisOnly)
- goodPt.v = lastGoodPt.v; // keep the vertical the same
- else // assume vAxisOnly
- goodPt.h = lastGoodPt.h; // keep the horizontal the same
- }
-
- // OK, we have a good point. if it's different than the last,
- // move the rgn
- if(goodPt.h != lastGoodPt.h || goodPt.v != lastGoodPt.v)
- {
- // Erase it, Move it, redraw it
- FrameRgn(theRgn);
- OffsetRgn(theRgn, goodPt.h - lastGoodPt.h, goodPt.v - lastGoodPt.v);
- FrameRgn(theRgn);
- }
- // remember the good point
- lastGoodPt = goodPt;
- }
- else // it's outside the slopRect
- {
- // if it just went outside...
- if(!outsideSlop)
- {
- // ...erase the rgn, and remember the point is outside
- FrameRgn(theRgn);
- outsideSlop = true;
- }
- }
- // remember the mouse position
- lastRawMouse = rawMouse;
- }
- }
-
- // If the final point is still outside the slopRect, return "nothing"
- if(outsideSlop)
- result.h = result.v = 0x8000;
- // Otherwise use the offset from the start to the existing good point,
- // and don't forget to erase the Rgn for the last time
- else
- {
- result.h = lastGoodPt.h - startPt.h;
- result.v = lastGoodPt.v - startPt.v;
- FrameRgn(theRgn);
- }
-
- // Restore pen
- SetPenState(&savedPen);
-
- return result;
- }
-
-
- void KeepPtInBounds(JuggleHandle aJuggle, gxPoint *point)
- {
- gxPoint grid = (*aJuggle)->gridPt;
-
- // Keep it from falling off the left edge
- if(point->x < 0)
- point->x = 0;
-
- // or the right edge (TWICE the juggle unit)
- else if(point->x > grid.x * ((*aJuggle)->numCounts * 2 - 1))
- point->x = grid.x * ((*aJuggle)->numCounts * 2 - 1);
-
- // and not off the bottom or top
- if(point->y < 0)
- point->y = 0;
- else if(point->y > grid.y * ((*aJuggle)->numJugglers - 1))
- point->y = grid.y * ((*aJuggle)->numJugglers - 1);
- }
-
- //----------------------------------------------------
- // grid enforcement routines
-
- // This routine returns the amount the given point is off the
- // given grid.
- void GetGridOffset(gxPoint *pointData, gxPoint *grid)
- {
- gxPoint localPt = *pointData;
-
- // Calculate the amount off grid (remainder of dividing by the grid)
- localPt.x %= grid->x;
- localPt.y %= grid->y;
-
- // If the amount off grid is greater than half the grid spacing,
- // (or less than its negative) then the point is
- // "before" the nearest grid point. Make it a small opposite signed
- // offset instead of a large one, so we can just subtract it from the
- // point to grid it.
- if(localPt.x < 0)
- {
- if(localPt.x < -(grid->x / 2))
- localPt.x = grid->x + localPt.x;
- }
- else
- {
- if(localPt.x > (grid->x / 2))
- localPt.x = localPt.x - grid->x;
- }
- if(localPt.y < 0)
- {
- if(localPt.y < -(grid->y / 2))
- localPt.y = grid->y + localPt.y;
- }
- else
- {
- if(localPt.y > (grid->y / 2))
- localPt.y = localPt.y - grid->y;
- }
-
- *pointData = localPt;
- }
-
- // This routine returns the amount the given point is off the
- // given grid. (integer coords version)
- void GetShortGridOffset(Point *pointData, Point grid)
- {
- Point localPt = *pointData;
-
- // Calculate the amount off grid (remainder of dividing by the grid)
- localPt.h %= grid.h;
- localPt.v %= grid.v;
-
- // If the amount off grid is greater than half the grid spacing,
- // (or less than its negative) then the point is
- // "before" the nearest grid point. Make it a small opposite signed
- // offset instead of a large one, so we can just subtract it from the
- // point to grid it.
- if(localPt.h < 0)
- {
- if(localPt.h < -(grid.h / 2))
- localPt.h = grid.h + localPt.h;
- }
- else
- {
- if(localPt.h > (grid.h / 2))
- localPt.h = localPt.h - grid.h;
- }
- if(localPt.v < 0)
- {
- if(localPt.v < -(grid.v / 2))
- localPt.v = grid.v + localPt.v;
- }
- else
- {
- if(localPt.v > (grid.v / 2))
- localPt.v = localPt.v - grid.v;
- }
-
- *pointData = localPt;
- }
-
- // This routine checks the given point to see if it's
- // within detentLimit of a grid point. If it is, the
- // point is set to the grid point, and true is returned.
- // Otherwise it's unchanged and false is returned.
- Boolean Detent(gxPoint *pointData, gxPoint *grid, Fixed detentLimit)
- {
- gxPoint localPt = *pointData;
- Boolean result;
-
- // Calculate the amount off grid
- GetGridOffset(&localPt, grid);
-
- // Check for a touch. If so, grid the point and return true.
- if(fixAbs(localPt.x) < detentLimit && fixAbs(localPt.y) < detentLimit)
- {
- pointData->x -= localPt.x;
- pointData->y -= localPt.y;
- result = true;
- }
- else
- result = false;
-
- return result;
- }
-
- // This routine forces the given point to the nearest grid position,
- // then converts it to a 1-based grid location instead of a location in
- // the view port's coordinates.
- void SetGridPos(gxPoint *pointData, gxPoint *grid)
- {
- gxPoint localPt = *pointData;
-
- // Calculate the amount off grid
- GetGridOffset(&localPt, grid);
-
- // Force point to nearest grid point
- pointData->x -= localPt.x;
- pointData->y -= localPt.y;
-
- // Convert to a 1-based grid location, fixed
- pointData->x /= grid->x; // integer result
- pointData->x = ff(pointData->x); // convert back to fixed
- pointData->y /= grid->y; // integer result
- pointData->y = ff(pointData->y); // convert back to fixed
- }
-
- //----------------------------------------------------
- // Screen to memory conversion routines
-
- // change the jugglerMap to reflect a moved juggler
- void MoveJugglerInMap(JuggleHandle aJuggle, short whichJuggler,
- short fromRow, short toRow)
- {
- short rowDistance, increment, index;
-
- // Sanity Check
- if(whichJuggler != RowToJuggler(aJuggle, fromRow))
- {
- SysBeep(10);
- return;
- }
-
- // see how far we moved
- rowDistance = toRow - fromRow;
-
- index = fromRow - 1;
- if(rowDistance < 0)
- {
- // moving up
- increment = -1;
- rowDistance = -rowDistance;
- }
- else
- {
- // moving down
- increment = 1;
- }
-
- // Move the block of jugglers between the from and to rows
- while(rowDistance-- > 0)
- {
- (*aJuggle)->jugglerMap[index] = (*aJuggle)->jugglerMap[index + increment];
- index += increment;
- }
-
- // insert moved juggler in destination row
- (*aJuggle)->jugglerMap[toRow - 1] = whichJuggler;
- }
-
- // Convert a juggler number to a row in the onscreen picture
- short JugglerToRow(JuggleHandle aJuggle, short juggler)
- {
- short count;
-
- // Brute force and awkwardness: Go through the map looking for this juggler
- for(count = 0; count < (*aJuggle)->numJugglers; count++)
- {
- if((*aJuggle)->jugglerMap[count] == juggler)
- {
- // Found it
- return (count + 1);
- }
- }
- return 0;
- }
-
- // Convert a row in the onscreen picture to a juggler number
- short RowToJuggler(JuggleHandle aJuggle, short row)
- {
- // range check first
- if(row < 1 || row > (*aJuggle)->numJugglers)
- return 0;
- return (*aJuggle)->jugglerMap[row - 1];
- }
-
- // Convert a HandLoc to a point in the view port
- void HandLocToPoint(JuggleHandle aJuggle, HandLoc *hand, gxPoint *point)
- {
- gxPoint grid = (*aJuggle)->gridPt;
-
- // The x coord is simply (time - 1) * xgrid.
- point->x = (hand->time - 1) * grid.x;
-
- // Get the point by first getting the right row, then multiplying by
- // the grid spacing.
- point->y = (JugglerToRow(aJuggle, hand->juggler) - 1) * grid.y;
- }
-
- // Convert a point in the viewPort to a HandLoc (no rounding, the point is
- // assumed to be gridded already)
- void PointToHandLoc(JuggleHandle aJuggle, gxPoint *point, HandLoc *hand,
- Boolean wrapIt)
- {
- gxPoint grid = (*aJuggle)->gridPt;
-
- // The time is simply the point's x coord divided by the grid.
- // (adjusted up for a 1-based time location)
- // Divide a Fixed by a Fixed and you get an integer result
- hand->time = (point->x / grid.x) + 1;
- // range check
- if(hand->time == 0) // label hit
- hand->time = -1; // signal to caller that label was hit
- // if off the right edge
- else if(hand->time > (*aJuggle)->numCounts)
- {
- // if wrapping, make it a legal time, else out of range, make it 0
- if(wrapIt)
- {
- hand->time %= (*aJuggle)->numCounts; // keep in range
- // !!! if it's zero, we've hit a special case: the last faded
- // juggler on the right. It should really wrap to the last solid
- // juggler on the right.
- if(hand->time == 0)
- hand->time = (*aJuggle)->numCounts;
- }
- else
- hand->time = 0;
- }
- else if(hand->time < 0)
- hand->time = 0;
-
- // Get the juggler by first dividing to get the (1-based) row, then converting
- hand->juggler = RowToJuggler(aJuggle, (point->y / grid.y) + 1);
- }
-
- // Given the point on screen, return a pointer to the specific hand
- // (no rounding, the point is assumed to be "gridded" already)
- HandPtr PointToHand(JuggleHandle aJuggle, gxPoint *point, Boolean wrapIt)
- {
- HandLoc aLoc;
-
- PointToHandLoc(aJuggle, point, &aLoc, wrapIt);
- return GetHand(aJuggle, aLoc, wrapIt);
- }
-
- // Convenience routine that takes a gxLine instead of two HandLocs
- void LineToAddedThrow(JuggleHandle aJuggle, gxLine *line)
- {
- HandLoc from, to;
- gxLine gridLine;
-
- // Line doesn't really reach grid points, so grid it
- gridLine = *line;
- gridLine.first.x -= kHandRadius;
- gridLine.last.x += kHandRadius;
-
- // convert points to HandLocs, wrapping them around
- PointToHandLoc(aJuggle, &gridLine.first, &from, true);
- PointToHandLoc(aJuggle, &gridLine.last, &to, true);
-
- AddThrow(aJuggle, from, to);
- }
-
- // Convenience routine that takes a gxLine instead of two HandLocs
- void LineToRemovedThrow(JuggleHandle aJuggle, gxLine *line)
- {
- HandLoc from, to;
- gxLine gridLine;
-
- // Line doesn't really reach grid points, so grid it
- gridLine = *line;
- gridLine.first.x -= kHandRadius;
- gridLine.last.x += kHandRadius;
-
- // convert points to HandLocs, wrapping them around
- PointToHandLoc(aJuggle, &gridLine.first, &from, true);
- PointToHandLoc(aJuggle, &gridLine.last, &to, true);
-
- RemoveThrow(aJuggle, from, to);
- }
-
- //----------------------------------------------------
- // Dialog routines
-
- // Ask the user how big to make the juggle, return zero if they cancel
- void DoJuggleSizeDialog(short *numJugglers, short *numCounts)
- {
- DialogPtr theDlog;
- short hit, kind;
- Handle itmhndl;
- Rect rect;
- GrafPtr oldport;
- UserItemUPP outlineUPP;
- ModalFilterUPP numFilterUPP;
- Boolean exitDialog = false;
- short startJugglers = kDefaultJugglers, startCounts = kDefaultCounts;
-
- WindowPtr frontWind;
- JuggleHandle aJuggle;
-
-
- // Assume failure
- *numJugglers = *numCounts = 0;
-
- /* Save the current port */
- GetPort(&oldport);
-
- // Get the size of the front juggle, if there is one.
- frontWind = FrontWindow();
- aJuggle = GetWindowJuggle(frontWind);
- if(aJuggle != nil)
- {
- startJugglers = (*aJuggle)->numJugglers;
- startCounts = (*aJuggle)->numCounts;
- }
-
- /* Get the dialog (invisible) */
- theDlog = GetNewDialog(kJuggleSizeDialogId, nil, (WindowPtr)(-1));
- if(theDlog == nil)
- return;
-
- // Set up UPPs
- outlineUPP = NewUserItemProc(BtnItem);
- numFilterUPP = NewModalFilterProc(NumFilter);
-
- /* Install OK button Outline */
- GetDialogItem(theDlog, iOKOutline, &kind, &itmhndl, &rect);
- SetDialogItem(theDlog, iOKOutline, userItem, (Handle)outlineUPP, &rect);
-
- /* Other user Items... */
-
- /* Set Values of text items... */
- ShortToDlog(startJugglers, theDlog, iNumJugglers);
- ShortToDlog(startCounts, theDlog, iNumCounts);
-
- /* Do it... */
- SelectDialogItemText(theDlog, iNumJugglers, 0, 32767);
- ShowWindow(theDlog);
- while(!exitDialog) /* Loop until break...*/
- {
- ModalDialog(numFilterUPP, &hit); /* NumFilter only allows numeric input */
- switch(hit)
- {
- case iOKButton: /* OK button hit...*/
- if(JuggleSizeOK(theDlog))
- {
- *numJugglers = DlogToShort(theDlog, iNumJugglers);
- *numCounts = DlogToShort(theDlog, iNumCounts);
- // Make sure numCounts is even
- if((*numCounts % 2) == 1)
- *numCounts += 1;
- exitDialog = true;
- }
- break;
-
- case iCancelButton: /* Cancel button hit...*/
- exitDialog = true;
- break;
-
- default: /* Anything else hit... */
- continue;
- }
- }
- SetCursor(&qd.arrow);
-
- // Dispose UPPs
- DisposeRoutineDescriptor(outlineUPP);
- DisposeRoutineDescriptor(numFilterUPP);
-
- DisposDialog(theDlog);
- }
-
- // This routine checks the size of the juggle, if it's not OK it beeps, selects the text
- // that needs fixing, and returns false.
- Boolean JuggleSizeOK(DialogPtr dptr)
- {
- short type, theNum;
- Rect rect;
- Handle hndl;
- Boolean rslt = false;
-
- // Check num jugglers
- GetDItem(dptr, iNumJugglers, &type, &hndl, &rect);
- theNum = DlogToShort(dptr, iNumJugglers);
- if(theNum < 1 || theNum > kMaxJugglers)
- {
- SysBeep(10);
- SelectDialogItemText(dptr, iNumJugglers, 0, 32767);
- return false;
- }
-
- // Check num counts
- GetDItem(dptr, iNumCounts, &type, &hndl, &rect);
- theNum = DlogToShort(dptr, iNumJugglers);
- if(theNum < 1 || theNum > kMaxCounts)
- {
- SysBeep(10);
- SelectDialogItemText(dptr, iNumCounts, 0, 32767);
- return false;
- }
- return true;
- }
-
- //----------------------------------------------------
- // String utility routines, from DTS
-
-
- /* Return the length of the c-string. (Same as strlen, but this function isn't
- ** part of the string library. The entire library may be more than you wish to
- ** link into the code segment that holds main, so this (and other) standard
- ** library function has been duplicated here. */
- short clen(char *cptr)
- {
- short i;
-
- for (i = 0; cptr[i]; ++i) {};
- return(i);
- }
-
- /* Catenate two c-strings. */
- char *ccat(char *s1, char *s2)
- {
- ccpy(s1 + clen(s1), s2);
- return(s1);
- }
-
- /* Copy a c-string. */
- char *ccpy(char *s1, char *s2)
- {
- char *c1, *c2;
-
- c1 = s1;
- c2 = s2;
- while ((*c1++ = *c2++) != 0) {};
- return(s1);
- }
-
- /* Convert a pascal-string to a c-string. */
- void p2c(StringPtr cptr)
- {
- unsigned char len;
-
- BlockMove(cptr + 1, cptr, len = *cptr);
- cptr[len] = 0;
- }
-
- short min(short a, short b)
- {
- if(b < a)
- return b;
- else
- return a;
- }
-